//
// Created by 王乐 on 2022/2/8.
//

#include "LeCamera2DBehaviour.h"

#include "glm/gtc/matrix_transform.hpp"

#include "LeInput.h"
#include "LeTime.h"
#include "LeScreenSettings.h"

#include "LeTransform.h"
#include "LeGameObject.h"

#include "LeUtils.h"

LeCamera2DBehaviour::LeCamera2DBehaviour()
{}

LeCamera2DBehaviour::~LeCamera2DBehaviour()
{

}

void LeCamera2DBehaviour::Start()
{
    m_FollowRatio = 0.2f;
    m_bIsPerspective = true;
    mouseSensitivity = 0.1f;
    zoom = 45.0f;
    m_bFreeMove = false;
    m_MoveSpeed = 2.5f;
    m_FollowOffset = glm::vec3(0, 0, 0);

    m_ShakeDuration = 0.2;
    m_ShakeTargetChangeTime = 0.05;
    m_ShakeStrength = 0.5;
    m_bShakeMode = false;
    m_CurShakeDuration = 0;
    m_CurShakeTargetChangeTime = 0;
}

void LeCamera2DBehaviour::Update()
{
    if(m_bFreeMove)
    {
        //Process Keyboard input
        //----------------------
        if (LeInput::getKey(GLFW_KEY_UP) == GLFW_PRESS)
            processKeyboard(C2M_UP);
        if (LeInput::getKey(GLFW_KEY_DOWN) == GLFW_PRESS)
            processKeyboard(C2M_DOWN);
        if (LeInput::getKey(GLFW_KEY_LEFT) == GLFW_PRESS)
            processKeyboard(C2M_LEFT);
        if (LeInput::getKey(GLFW_KEY_RIGHT) == GLFW_PRESS)
            processKeyboard(C2M_RIGHT);

        //Process Mouse scroll
        //--------------------
        processMouseScroll(LeInput::getScrollYOffset());
    }
    else
    {
        if(m_pTargetGameObject != nullptr)
        {
            glm::vec3 targetpos = m_pTargetGameObject->m_Transform.m_Position + m_FollowOffset;

            if(m_bShakeMode)
            {
                m_CurShakeDuration += LeTime::dDeltaTime;
                if(m_CurShakeDuration > m_ShakeDuration)
                {
                    m_bShakeMode = false;
                    m_CurShakeDuration = 0;
                }

                m_CurShakeTargetChangeTime += LeTime::dDeltaTime;
                if(m_CurShakeTargetChangeTime > m_ShakeTargetChangeTime)
                {
                    m_CurShakeTargetChangeTime = 0;
                    glm::vec3 offset = glm::vec3(LeMath::getRandomF(-m_ShakeStrength, m_ShakeStrength),
                                                 LeMath::getRandomF(-m_ShakeStrength, m_ShakeStrength),
                                                 LeMath::getRandomF(-m_ShakeStrength, m_ShakeStrength));
                    targetpos += offset;
                }
            }

            m_pTransform->m_Position.x =
                    m_pTransform->m_Position.x + m_FollowRatio * (targetpos.x - m_pTransform->m_Position.x);
            m_pTransform->m_Position.y =
                    m_pTransform->m_Position.y + m_FollowRatio * (targetpos.y - m_pTransform->m_Position.y);
        }
    }
}

glm::mat4 LeCamera2DBehaviour::getViewMatrix()
{
    return glm::lookAt(m_pTransform->m_Position,
                       m_pTransform->m_Position + glm::vec3(0.f,0.f,-1.f),
                       glm::vec3(0.f,1.f,0.f));
}

glm::mat4 LeCamera2DBehaviour::getProjectionMatrix()
{
    if(m_bIsPerspective)
        return glm::perspective(glm::radians(zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
    else
        return glm::ortho(-2.0f*zoom*mouseSensitivity, 2.0f*zoom*mouseSensitivity,
                          -3.0f*zoom*mouseSensitivity, 3.0f*zoom*mouseSensitivity,
                          0.1f*zoom*mouseSensitivity, 15.0f*zoom*mouseSensitivity);
}

void LeCamera2DBehaviour::processKeyboard(Camera2D_Movement direction)
{
    float velocity = m_MoveSpeed * LeTime::dDeltaTime;
    switch (direction)
    {
        case C2M_UP:    m_pTransform->m_Position += glm::vec3(0.f, 1.f, 0.f) * velocity; break;
        case C2M_DOWN:  m_pTransform->m_Position -= glm::vec3(0.f, 1.f, 0.f) * velocity; break;
        case C2M_LEFT: m_pTransform->m_Position -= glm::vec3(1.f, 0.f, 0.f) * velocity; break;
        case C2M_RIGHT: m_pTransform->m_Position += glm::vec3(1.f, 0.f, 0.f) * velocity; break;
    }
}

void LeCamera2DBehaviour::processMouseScroll(float yoffset)
{
    zoom -= (float)yoffset;
    if (zoom < 1.0f)
        zoom = 1.0f;
    if (zoom > 45.0f)
        zoom = 45.0f;
}

void LeCamera2DBehaviour::setCameraTarget(LeGameObject *target)
{
    m_pTargetGameObject = target;
}

void LeCamera2DBehaviour::setCameraShakeMode(bool isShakeMode)
{
    m_bShakeMode = isShakeMode;
}
